package com.gooluke.redismap.config;

import com.gooluke.redismap.entity.StringRedisData;
import com.gooluke.redismap.properties.RedisMapProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @author gooluke
 * description
 * datetime 2025-09-14 17:01
 */
public class StringRedisMapManager implements DisposableBean, ApplicationRunner {

    private final Logger log = LoggerFactory.getLogger(StringRedisMapManager.class);

    private final Map<String, StringRedisData> stringRedisDataMap = new ConcurrentHashMap<>();
    private final ScheduledExecutorService cleanupScheduler = Executors.newSingleThreadScheduledExecutor();
    private final AtomicLong hitCount = new AtomicLong(0);
    private final AtomicLong missCount = new AtomicLong(0);

    private final RedisMapProperties redisMapProperties;

    @Autowired
    public StringRedisMapManager(RedisMapProperties redisMapProperties) {
        this.redisMapProperties = redisMapProperties;
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        init();
    }

    private void init() {
        RedisMapProperties.DeleteConfig deleteConfig = redisMapProperties.getDelete();
        log.info("StringRedisMapManager init... initialDelay: {} , period: {}", deleteConfig.getInitialDelay(), deleteConfig.getPeriod());
        // 启动定期清理过期数据的任务
        cleanupScheduler.scheduleAtFixedRate(this::cleanupExpiredData,
                deleteConfig.getInitialDelay(), deleteConfig.getPeriod(), TimeUnit.SECONDS); // 每十分钟清理一次
    }

    public String get(String key) {
        StringRedisData data = stringRedisDataMap.get(key);
        if (data == null) {
            missCount.incrementAndGet();
            return null;
        }

        if (data.isExpired()) {
            // 使用computeIfPresent确保原子性删除
            stringRedisDataMap.computeIfPresent(key, (k, v) -> v.isExpired() ? null : v);
            missCount.incrementAndGet();
            return null;
        }

        hitCount.incrementAndGet();
        return data.getValue();
    }

    public boolean set(String key, String value) {
        return set(key, value, 0);
    }

    public boolean set(String key, String value, long expireSeconds) {
        long expireTime = expireSeconds > 0 ? System.currentTimeMillis() + expireSeconds * 1000 : 0;
        stringRedisDataMap.put(key, new StringRedisData(value, expireTime));
        return true;
    }

    public boolean setNx(String key, String value) {
        return setNx(key, value, 0);
    }

    public boolean setNx(String key, String value, long expireSeconds) {
        // 使用putIfAbsent确保原子性操作
        StringRedisData existingData = stringRedisDataMap.get(key);
        if (existingData != null && !existingData.isExpired()) {
            return false;
        }

        long expireTime = expireSeconds > 0 ? System.currentTimeMillis() + expireSeconds * 1000 : 0;
        StringRedisData newData = new StringRedisData(value, expireTime);

        // 原子操作：如果key不存在或已过期，则设置新值
        StringRedisData result = stringRedisDataMap.putIfAbsent(key, newData);
        if (result == null) {
            return true;
        }

        // 如果putIfAbsent返回了数据，检查是否过期
        if (result.isExpired()) {
            return stringRedisDataMap.replace(key, result, newData);
        }

        return false;
    }

    public boolean del(String key) {
        StringRedisData removed = stringRedisDataMap.remove(key);
        return removed != null;
    }

    public long ttl(String key) {
        StringRedisData data = stringRedisDataMap.get(key);
        if (data == null) {
            return -2; // key不存在
        }
        if (data.getExpireTime() == 0) {
            return -1; // 永久key
        }
        if (data.isExpired()) {
            stringRedisDataMap.remove(key);
            return -2; // key已过期
        }
        return (data.getExpireTime() - System.currentTimeMillis()) / 1000;
    }

    /**
     * 清理过期数据
     */
    private void cleanupExpiredData() {
        log.debug("cleanupExpiredData start...");
        long startTime = System.currentTimeMillis();
        int removedCount = 0;

        for (Map.Entry<String, StringRedisData> entry : stringRedisDataMap.entrySet()) {
            if (entry.getValue().isExpired()) {
                // 使用remove(key, value)确保原子性
                if (stringRedisDataMap.remove(entry.getKey(), entry.getValue())) {
                    removedCount++;
                }
            }
        }

        long endTime = System.currentTimeMillis();
        log.debug("Cleanup completed, removed {} expired keys, took {} ms", removedCount, (endTime - startTime));
    }

    /**
     * 获取缓存统计信息
     */
    public Map<String, Object> getStats() {
        return Map.of(
                "totalKeys", stringRedisDataMap.size(),
                "hitCount", hitCount.get(),
                "missCount", missCount.get(),
                "hitRate", hitCount.get() + missCount.get() > 0 ?
                        (double) hitCount.get() / (hitCount.get() + missCount.get()) : 0
        );
    }

    /**
     * 优雅关闭
     */
    public void shutdown() {
        cleanupScheduler.shutdown();
        try {
            if (!cleanupScheduler.awaitTermination(5, TimeUnit.SECONDS)) {
                cleanupScheduler.shutdownNow();
            }
        } catch (InterruptedException e) {
            cleanupScheduler.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }

    @Override
    public void destroy() throws Exception {
        shutdown();
    }
}